/************************************************************************
  DISCLAIMER:
  This software was produced by the National Institute of Standards
  and Technology (NIST), an agency of the U.S. government, and by statute
  is not subject to copyright in the United States.  Recipients of this
  software assume all responsibility associated with its operation,
  modification, maintenance, and subsequent redistribution.

  See NIST Administration Manual 4.09.07 b and Appendix I.
************************************************************************/

#include <string.h>  // for strlen, strcpy, strcat
#include <stdio.h>   // for fopen, etc.
#include <stdlib.h>  // for exit
#include "dmis.hh"
#include "cs1Lists.cc"
#include "cs2Lists.cc"
#include "cs3Lists.cc"
#include "ipv1Lists.cc"
#include "ipv2Lists.cc"
#include "ipv3Lists.cc"
#include "mc1Lists.cc"
#include "mc2Lists.cc"
#include "mc3Lists.cc"
#include "p1Lists.cc"
#include "p2Lists.cc"
#include "p3Lists.cc"
#include "qis1Lists.cc"
#include "qis2Lists.cc"
#include "qis3Lists.cc"
#include "rt1Lists.cc"
#include "rt2Lists.cc"
#include "rt3Lists.cc"
#include "sga1Lists.cc"
#include "sga2Lists.cc"
#include "sga3Lists.cc"
#include "tw1Lists.cc"
#include "tw2Lists.cc"
#include "tw3Lists.cc"
#include "unc1Lists.cc"
#include "unc2Lists.cc"
#include "unc3Lists.cc"

namespace NDTS{
  extern int numErrors;
  extern int numWarnings;
  extern inputFile * tree;
  extern std::list<dmisStatement *> dmisStms;
  void parseDmis(char * fileName);
  void resetParser();
}
namespace NDTU {
  using namespace NDTS;

/*

warnAtt and warnSub are not functions (with toPrint a dmisFullCppBase)
because when toPrint has multiple supertypes, ambiguous subtype errors
are caused by using the printSelf function.

For both macros, the call should be preceded by a left brace and
followed by a semicolon and a right brace (so the macro call looks a
normal function call).

The if(1){...}else alternative for this style of macro is not better
because it is more complex and still requires braces to avoid a
compiler warning if it is used after an "if" that does not have an
"else".

*/

#define warnAtt(attrib, className, toPrint)              \
   numErrors++;                                          \
   printf("no %s attribute of %s\n", attrib, className); \
   toPrint->printSelf();                                 \
   printf("\n\n")

#define warnAttBlock(statement, toPrint)		      \
   numErrors++;                                               \
   printf("%s is not in the conformance class\n", statement); \
   toPrint->printSelf();                                      \
   printf("\n")

#define warnSub(sub, super, toPrint)            \
   numErrors++;                                 \
   printf("no %s subtype of %s\n", sub, super); \
   toPrint->printSelf();                        \
   printf("\n\n")

#define warnBlockBug(sub, super)               \
   printf("bug: %s subtype of %s should be "   \
          "allowed but isn't\n\n", sub, super) 

const char ** masterSubAtts[1650];
const char * theStatements[300];

/********************************************************************/

// function defined in assignModuleSubAtts.cc
void assignModuleSubAtts();
// function defined in assignMasterSubAtts.cc
void assignMasterSubAtts();

// functions defined in dmisConformanceChecker.cc
void analyzeItems(std::list<dmisStatement *> * items);
void check_inputFile(inputFile * a_inputFile);
void reportSummary();
void reportSummaryFull();

// functions defined here hard-coded
void addAtEnd(const char ** addTo, const char ** from);
void addIfNew(const char * aString, const char ** strings);
int checkDmis(int argc, char * argv[]);
void checkManyFiles(char * fileName, int argc);
void checkOneFile(char * fileName, int argc);
void combineLists(const char *** mainList, const char **** otherLists,
		  int otherListsSize);
bool dupAddendum(const char *** list1, const char *** list2,
		 const char *** list3, const char **** otherLists,
		 int otherListsSize);
void findStatements();
bool findString(const char * toFind, const char * lookIn[]);
void installList(const char *** aList);
bool prepareLists(int addenda, char ** args);

/********************************************************************/

/* addAtEnd

Returned Value: none

Called By: installList

This copies the string pointers of the from array that do not have the
same text as any element of the addTo array onto the end of the addTo
array (i.e. it does not copy duplicates). This starts with the second
string in the from array since the first string represents the array
name. This is not checking array sizes.  Both arrays must be
zero-terminated.

The strings themselves are not copied.

*/
void addAtEnd(        /* ARGUMENTS                    */
 const char ** addTo, /* array of strings to add to   */
 const char ** from)  /* array of strings to add from */
{
  int m;  // index for addTo
  int n;  // index from from

  for (n = 1; from[n]; n++)
    {
      for (m = 1; addTo[m]; m++)
	{
	  if (strcmp(from[n], addTo[m]) == 0) // from[n] duplicates addTo[m]
	    break; 	}
      if (addTo[m]) // found a duplicate so do not add it
	continue;
      else // from[n] is not a duplicate, so add it to end of addTo list
	{
	  addTo[m] = from[n];
	  addTo[m+1] = 0;
	}
    }
}

/********************************************************************/

/* addIfNew

Returned Value: none

Called By: findStatements

This adds aString to strings in alphabetical order if aString is not
already included in strings. Space is made to insert a new string by
shifting to the right all the strings that should come after the new
string.

This works OK if strings is empty and if aString should be the
last string in strings.

The strings array must have a 0 after the last string.

*/

void addIfNew(
 const char * aString,
 const char ** strings)
{
  static int end = 0;  // index of the 0 following the last string in strings
  int k;               // counter and index of the place to insert aString
  int m;               // shifting index
  int result;          // value returned by strcmp

  for (k=0; strings[k]; k++)
    {
      result = strcmp(aString, strings[k]);
      if (result == 0) // duplicate found; do nothing
	return;
      else if (result < 0)
	{ // aString should be inserted immediately after strings[k].
	  for (m=end; m > k; m--)
	    strings[m] = strings[m-1];
	  strings[k] = aString;
	  end++;
	  strings[end] = 0;
	  return;
	}
    }
  // if we get this far, aString goes at the end
  strings[end] = aString;
  end++;
  strings[end] = 0;
}

/********************************************************************/

/* checkManyFiles

Returned Value: none

Called By: checkDmis

This expects that the file whose name is fileNameFile will contain the
names of a number of DMIS input files.

For each file listed in the fileNameFile, this calls checkOneFile.

*/

void checkManyFiles(  /* ARGUMENTS                                     */
 char * fileNameFile, /* name of file containing DMIS input file names */
 int conform)         /* number of conformance specs                   */
{
  FILE * fileList;
  static char fileName[256];
  int nameLength;

  fileList = fopen(fileNameFile, "r");
  if (fileList == 0)
    {
      fprintf(stderr, "unable to open file %s for reading\n",
              fileNameFile);
      exit(1);
    }
  while (fgets(fileName, 256, fileList))
    {
      nameLength = strlen(fileName);
      if (nameLength == 255)
	{
	  fprintf(stderr, "file name too long: %s\n", fileName);
	  exit(1);
        }
      while ((fileName[nameLength - 1] == 10) ||
             (fileName[nameLength - 1] == 13))
	{ // get rid of the end of line character(s)
	  fileName[nameLength - 1] = 0;
	  nameLength--;
	}
      if (strcmp((fileName + nameLength - 4), ".dmi"))
	{
	  fprintf(stderr, "file name does not end in .dmi: %s\n",
                  fileName);
	  exit(1);
	}
      printf("*****************************************\n\n");
      printf("%s\n\n", fileName);
      checkOneFile(fileName, conform);
    }
  fclose(fileList);
}

/********************************************************************/

/* checkOneFile

Returned Value: none

Called By:
  checkManyFiles
  checkDmis

This checks one DMIS input file as follows:
1. Call parseDmis, which checks for syntax errors. If there are no errors,
   the parser also builds a parse tree named "tree".
2. Report the number of errors and warnings for the file.
3. Call analyzeItems, which counts the number of times each DMIS statement
   is used (and does not use the tree).
4. If there are no errors and conform is non-zero (indicating a
   conformance class is being used, not full DMIS):
   a. Call check_inputFile, which checks whether the tree contains items
      that are not in the conformance class. For each item used but not in
      the conformance class (if there are any such items), an error message
      is printed and the item in the tree that caused the error is printed
      out as DMIS code.
   b. Report the number of conformance class errors found in the file.
5. Clear the dmisStms list.
6. Call resetParser to reset the parser.

*/

void checkOneFile( /* ARGUMENTS                             */
 char * dmiName,   /* name of DMIS input file to check      */
 int conform)      /* number of conformance specs, may be 0 */
{
  parseDmis(dmiName);
  printf("%d parser error%s\n", numErrors, ((numErrors == 1) ? "" : "s"));
  printf("%d parser warning%s\n\n", numWarnings,
	  ((numWarnings == 1) ? "" : "s"));
  analyzeItems(&dmisStms);
  if (conform && (numErrors == 0))
    {
      check_inputFile(tree);
      printf("%d conformance checker error%s\n\n",
	      numErrors, ((numErrors == 1) ? "" : "s"));
    }
  dmisStms.clear();
  resetParser();
}

/********************************************************************/

/* combineLists

Returned Value: none

Called By: prepareLists

This copies the mainList into masterSubAtts and then copies each other list
into masterSubAtts. See documentation of installList.

*/

void combineLists(           /* ARGUMENTS                                   */
 const char *** mainList,    /* array of arrays of strings to start with    */
 const char **** otherLists, /* array of arrays of arrays of strings to add */
 int otherListsSize)         /* size of otherLists                          */
{
  int n;

  installList(mainList);
  for (n = 0; n < otherListsSize; n++)
    {
      installList(otherLists[n]);
    }
}

/********************************************************************/

/* dupAddendum

Returned Value: bool
If any of the arrays list1, list2, and list3 is already in the otherLists,
this returns true (indicating a duplicate). Otherwise it returns false.

Called By: prepareLists

*/

bool dupAddendum(
 const char *** list1,
 const char *** list2,
 const char *** list3,
 const char **** otherLists,
 int otherListsSize)
{
  int n;

  for (n = 0; n < otherListsSize; n++)
    {
      if ((otherLists[n] == list1) ||
	  (otherLists[n] == list2) ||
	  (otherLists[n] == list3))
	return true;
    }
  return false;
}

/********************************************************************/

/* findStatements

Returned Value: none

Called By: checkDmis

This builds the theStatements list from masterSubAtts (which must have been
constructed previously). Those are both global variables. This is
semi-hard-coded because doing it any other way would be a pain (since
several statements are hidden). masterSubAtts is in alphabetical order, and
theStatements is built in alphabetical order (so that reporting can be
done without hunting around for names).  This goes through masterSubAtts in
order, looking at the names arrays listed below and adding the names
listed below to theStatements if they are found in a names array. 
endfilStm, which has no entry in masterSubAtts (because it has neither
attributes nor subtypes) is added at the beginning. This does not add
duplicates to theStatements.

1.  calibMasterBlockAtts   - calibMasterStm, endmesStm
2.  calibRtabBlockAtts     - calibRtabStm, endmesStm
3.  calibSensBlockAtts     - calibSensStm, endmesStm
4.  callBlockAtts          - callStm
5.  caseBlockAtts          - caseStm, endcasStm
6.  defaultCaseBlockAtts   - dftcasStm
7.  dmisFirstStatementSubs - all entries after the first
8.  dmisFreeStatementSubs  - all entries after the first
9.  dmisOffBlockAtts       - dmisOffStm, dmisOnStm
10. doBlockAtts            - doStm, enddoStm
11. gotargBlockAtts        - gotargStm, endgoStm
12. ifBlockAtts            - ifStm, elseStm, endifStm
13. macroBlockAtts         - macroStm, endmacStm
14. measBlock_measStmAtts  - measStm, endmesStm
15. measBlock_rmeasStmAtts - rmeasStm, endmesStm
16. selectBlockAtts        - selectStm, endselStm
17. simreqtBlockAtts       - simreqtStm, endsimreqtStm
18. xternBlockAtts         - xternStm, extfilStm, endxtnStm

All of the items on the left above must be found (by "while") in
masterSubAtts, or this will crash -- so if there are major revisions to
DMIS, this will need to be updated.

*/

void findStatements()
{
  int n; // counter for arrays of masterSubAtts
  int k; // counter for strings in one array of strings

  theStatements[0] = 0;
  addIfNew("endfilStm", theStatements);
  n = -1;
  while (strcmp(masterSubAtts[++n][0], "calibMasterBlockAtts"));
  for (k=1; masterSubAtts[n][k]; k++)
    {
      if (strcmp(masterSubAtts[n][k], "a_calibMasterStm") == 0)
	addIfNew("calibMasterStm", theStatements);
      else if (strcmp(masterSubAtts[n][k], "a_endmesStm") == 0)
	addIfNew("endmesStm", theStatements);
    }
  while (strcmp(masterSubAtts[++n][0], "calibRtabBlockAtts"));
  for (k=1; masterSubAtts[n][k]; k++)
    {
      if (strcmp(masterSubAtts[n][k], "a_calibRtabStm") == 0)
	addIfNew("calibRtabStm", theStatements);
      else if (strcmp(masterSubAtts[n][k], "a_endmesStm") == 0)
	addIfNew("endmesStm", theStatements);
    }
  while (strcmp(masterSubAtts[++n][0],"calibSensBlockAtts"));
  for (k=1; masterSubAtts[n][k]; k++)
    {
      if (strcmp(masterSubAtts[n][k], "a_calibSensStm") == 0)
	addIfNew("calibSensStm", theStatements);
      else if (strcmp(masterSubAtts[n][k], "a_endmesStm") == 0)
	addIfNew("endmesStm", theStatements);
    }
  while (strcmp(masterSubAtts[++n][0],"callBlockAtts"));
  for (k=1; masterSubAtts[n][k]; k++)
    {
      if (strcmp(masterSubAtts[n][k], "a_callStm") == 0)
	addIfNew("callStm", theStatements);
      else if (strcmp(masterSubAtts[n][k], "a_endmacStm") == 0)
	addIfNew("endmacStm", theStatements);
     }
  while (strcmp(masterSubAtts[++n][0],"caseBlockAtts"));
  for (k=1; masterSubAtts[n][k]; k++)
    {
      if (strcmp(masterSubAtts[n][k], "a_caseList") == 0)
	addIfNew("caseStm", theStatements);
      else if (strcmp(masterSubAtts[n][k], "a_endcasStm") == 0)
	addIfNew("endcasStm", theStatements);
    }
  while (strcmp(masterSubAtts[++n][0],"defaultCaseBlockAtts"));
  for (k=1; masterSubAtts[n][k]; k++)
    {
      if (strcmp(masterSubAtts[n][k], "a_dftcasStm") == 0)
	addIfNew("dftcasStm", theStatements);
      else if (strcmp(masterSubAtts[n][k], "a_endcasStm") == 0)
	addIfNew("endcasStm", theStatements);
    }
  while (strcmp(masterSubAtts[++n][0],"dmisFirstStatementSubs"));
  for (k=1; masterSubAtts[n][k]; k++)
    {
      addIfNew(masterSubAtts[n][k], theStatements);
    }
  while (strcmp(masterSubAtts[++n][0],"dmisFreeStatementSubs"));
  for (k=1; masterSubAtts[n][k]; k++)
    {
      addIfNew(masterSubAtts[n][k], theStatements);
    }
  while (strcmp(masterSubAtts[++n][0],"dmisOffBlockAtts"));
  for (k=1; masterSubAtts[n][k]; k++)
    {
      if (strcmp(masterSubAtts[n][k], "a_dmisOffStm") == 0)
	addIfNew("dmisOffStm", theStatements);
      else if (strcmp(masterSubAtts[n][k], "a_dmisOnStm") == 0)
	addIfNew("dmisOnStm", theStatements);
    }
  while (strcmp(masterSubAtts[++n][0],"doBlockAtts"));
  for (k=1; masterSubAtts[n][k]; k++)
    {
      if (strcmp(masterSubAtts[n][k], "a_doStm") == 0)
	addIfNew("doStm", theStatements);
      else if (strcmp(masterSubAtts[n][k], "a_enddoStm") == 0)
	addIfNew("enddoStm", theStatements);
    }
  while (strcmp(masterSubAtts[++n][0],"gotargBlockAtts"));
  for (k=1; masterSubAtts[n][k]; k++)
    {
      if (strcmp(masterSubAtts[n][k], "a_gotargStm") == 0)
	addIfNew("gotargStm", theStatements);
      else if (strcmp(masterSubAtts[n][k], "a_endgoStm") == 0)
	addIfNew("endgoStm", theStatements);
    }
  while (strcmp(masterSubAtts[++n][0],"ifBlockAtts"));
  for (k=1; masterSubAtts[n][k]; k++)
    {
      if (strcmp(masterSubAtts[n][k], "a_ifStm") == 0)
	addIfNew("ifStm", theStatements);
      else if (strcmp(masterSubAtts[n][k], "a_elseStm") == 0)
	addIfNew("elseStm", theStatements);
      else if (strcmp(masterSubAtts[n][k], "a_endifStm") == 0)
	addIfNew("endifStm", theStatements);
    }
  while (strcmp(masterSubAtts[++n][0],"macroBlockAtts"));
  for (k=1; masterSubAtts[n][k]; k++)
    {
      if (strcmp(masterSubAtts[n][k], "a_macroStm") == 0)
	addIfNew("macroStm", theStatements);
      else if (strcmp(masterSubAtts[n][k], "a_endmacStm") == 0)
	addIfNew("endmacStm", theStatements);
    }
  while (strcmp(masterSubAtts[++n][0],"measBlock_measStmAtts"));
  for (k=1; masterSubAtts[n][k]; k++)
    {
      if (strcmp(masterSubAtts[n][k], "a_measStm") == 0)
	addIfNew("measStm", theStatements);
      else if (strcmp(masterSubAtts[n][k], "a_endmeasStm") == 0)
	addIfNew("endmesStm", theStatements);
    }
  while (strcmp(masterSubAtts[++n][0],"measBlock_rmeasStmAtts"));
  for (k=1; masterSubAtts[n][k]; k++)
    {
      if (strcmp(masterSubAtts[n][k], "a_rmeasStm") == 0)
	addIfNew("rmeasStm", theStatements);
      else if (strcmp(masterSubAtts[n][k], "a_endmeasStm") == 0)
	addIfNew("endmesStm", theStatements);
    }
  while (strcmp(masterSubAtts[++n][0],"selectBlockAtts"));
  for (k=1; masterSubAtts[n][k]; k++)
    {
      if (strcmp(masterSubAtts[n][k], "a_selectStm") == 0)
	addIfNew("selectStm", theStatements);
      else if (strcmp(masterSubAtts[n][k], "a_endselStm") == 0)
	addIfNew("endselStm", theStatements);
    }
  while (strcmp(masterSubAtts[++n][0],"simreqtBlockAtts"));
  for (k=1; masterSubAtts[n][k]; k++)
    {
      if (strcmp(masterSubAtts[n][k], "a_simreqtStm") == 0)
	addIfNew("simreqtStm", theStatements);
      else if (strcmp(masterSubAtts[n][k], "a_endsimreqtStm") == 0)
	addIfNew("endsimreqtStm", theStatements);
    }
  while (strcmp(masterSubAtts[++n][0],"xternBlockAtts"));
  for (k=1; masterSubAtts[n][k]; k++)
    {
      if (strcmp(masterSubAtts[n][k], "a_xternStm") == 0)
	addIfNew("xternStm", theStatements);
      else if (strcmp(masterSubAtts[n][k], "a_extfilList") == 0)
	addIfNew("extfilStm", theStatements);
      else if (strcmp(masterSubAtts[n][k], "a_endxtnStm") == 0)
	addIfNew("endxtnStm", theStatements);
    }
}

/********************************************************************/

/* findString

Returned Value: bool
If toFind is found in lookIn, this returns true.
Otherwise, it returns false.

Called By: hundreds of checking functions

This does string comparison using strcmp. It is not looking for
string identity.

Eventually, try to make this more efficient (maybe use 2-level array).

For this to work, the last entry in lookIn must be 0.

*/

bool findString(        /* ARGUMENTS                   */
 const char * toFind,   /* string to find              */
 const char * lookIn[]) /* array of strings to look in */
{
  const char ** wordPtr;

  for (wordPtr = lookIn; *wordPtr; wordPtr = (wordPtr + 1))
    {
      if (strcmp(toFind, *wordPtr) == 0)
	return true;
    }
  return false;
}

/********************************************************************/

/* installList

Returned Value: none

Called By: combineLists

This copies each sublist of the aList into the corresponding
sublist of masterSubAtts.

For this to work:
1. The sublists of both the aList and masterSubAtts must be in
   alphabetical order of the first entry in the sublist.
2. Every sublist of the aList must have a counterpart in masterSubAtts
   (but masterSubAtts may have sublists with no counterpart in aList).

The first entry in each sublist of masterSubAtts is a string that is the
same as the global variable name of the sublist. The first entry in
each sublist of aList is also the same as a global variable name
of some sublist. The two first entries are used to match sublists of
aList with sublists of masterSubAtts.

*/

void installList(
 const char *** aList)
{
  int m;  // index for aList
  int n;  // index for masterSubAtts
  
  n = 0;
  for (m = 0; aList[m]; m++)
    {
      for (; masterSubAtts[n]; n++)
	{
	  if (strcmp(aList[m][0], masterSubAtts[n][0]) == 0)
	    {
	      addAtEnd(masterSubAtts[n], aList[m]);
	      break;
	    }
	}
    }
}

/********************************************************************/

/* checkDmis

Returned Value: none

Called By:  not called here. Intended to be called in some other file.

This checks that there is at least one argument and exits if not.
The first argument the name of either a DMIS input file (with a
.dmi suffix) or a file containing the names of DMIS input files.
In the first case, this calls checkOnefile. In the second case it
calls checkManyFiles.

If there is more than one argument, the second argument must be
the code for a level of an AP. The following codes are allowed.
The meaning of each code is given in parentheses.
PM1 (Prismatic 1)
PM2 (Prismatic 2)
PM3 (Prismatic 3)
TW1 (Thin Walled 1)
TW2 (Thin Walled 2)
TW3 (ThinWalled3)

If there are more than two arguments, each remaining argument must
be the name of a level of an addendum. The following codes are allowed.
The meaning of each code is given in parentheses. At most one level of
each addendum may be used, for a maximum of 7 more arguments.
RY1 (RotaryTable 1)
RY2 (RotaryTable 2)
RY3 (RotaryTable 3)
MC1 (Multi Carriage 1)
MC2 (Multi Carriage 2)
MC3 (Multi Carriage 3)
CT1 (Contact Scanning 1)
CT2 (Contact Scanning 2)
CT3 (Contact Scanning 3)
IP1 (IPV 1)
IP2 (IPV 2)
IP3 (IPV 3)
QI1 (QIS 1)
QI2 (QIS 2)
QI3 (QIS 3)
MU1 (measurement uncertainty 1)
MU2 (measurement uncertainty 2)
MU3 (measurement uncertainty 3)
SF1 (soft gaging 1)
SF2 (soft gaging 2)
SF3 (soft gaging 3)

*/

int checkDmis(  /* ARGUMENTS                                   */
 int argc,      /* number of command arguments                 */
 char * argv[]) /* array of command name and command arguments */
{
  int k; // length of file name
  char * filename;
  int conform; // number of conformance specs

  if (argc < 2)
    {
      fprintf(stderr,
	  "Usage: %s <input file> [[<AP>] [<addendum> ...]]\n",
	      argv[0]);
      fprintf(stderr, "AP may be PM1, PM2, PM3, TW1, TW2, or TW3\n");
      fprintf(stderr, 
	      "addendum may be 0 or 1 from each of the following 7 sets\n");
      fprintf(stderr, 
	      "(RY1,RY2,RY3),(CT1,CT2,CT3),(MC1,MC2,MC3),(IP1,IP2,IP3)\n");
      fprintf(stderr, "(QI1,QI2,QI3),(MU1,MU2,MU3),(SF1,SF2,SF3)\n");
      exit(1);
    }
  if (argc == 2)
    {
      conform = 0;
    }
  else
    {
      conform = (argc - 2);
      if (prepareLists((argc - 3), (argv + 2)) == false)
	{
	  fprintf(stderr, "bad command arguments\n");
	  exit(1);
	}
      findStatements();
    }
  filename = argv[1];
  k = strlen(filename);
  if ((filename[k-4] == '.') &&
      ((filename[k-3] == 'd') || (filename[k-3] == 'D')) &&
      ((filename[k-2] == 'm') || (filename[k-2] == 'M')) &&
      ((filename[k-1] == 'i') || (filename[k-1] == 'I')))
    { // argv[1] is a DMIS input file name
      checkOneFile(argv[1], conform);
    }
  else
    { // argv[1] is the name of a file containing DMIS input file names
      checkManyFiles(argv[1], conform);
      if (conform)
	reportSummary();
      else
	reportSummaryFull();
    }
  return 0;
}

/********************************************************************/

/* prepareLists

Returned Value: bool
If any of the command arguments is bad, this returns false.
Otherwise, it returns true.

Called By: checkDmis

This checks that the second command argument is one of the allowed
6 values.

This also checks that each additional argument (which indicates
a specific level of a specific addendum) is one of the allowed 21
values and that at most one of each group of three is used. This has
the side effect of ensuring that there are not too many entries in the
otherLists array.

If the command arguments are OK, this sets the mainList and the
otherLists arrays and calls combineLists, which will use them to
install the contents of the sublists of masterSubAtts (a global array).

*/

bool prepareLists( /* ARGUMENTS                                     */
 int addenda,      /* number of addenda at end of command arguments */
 char ** args)     /* end of array of command arguments             */
{
  int k; // arg counter
  const char *** mainList;
  const char *** otherLists[9];

  assignModuleSubAtts();
  assignMasterSubAtts();
  if (strcmp(args[0], "PM1") == 0)
    mainList = p1Lists;
  else if (strcmp(args[0], "PM2") == 0)
    mainList = p2Lists;
  else if (strcmp(args[0], "PM3") == 0)
    mainList = p3Lists;
  else if (strcmp(args[0], "TW1") == 0)
    mainList = tw1Lists;
  else if (strcmp(args[0], "TW2") == 0)
    mainList = tw2Lists;
  else if (strcmp(args[0], "TW3") == 0)
    mainList = tw3Lists;
  else
    return false;
  args = (args + 1);
  for (k = 0; k < addenda; k++)
    {
      if (strcmp(args[k], "CT1")  == 0)
	{
	  if (dupAddendum(cs1Lists, cs2Lists, cs3Lists, otherLists, k))
	    return false;
	  otherLists[k] = cs1Lists;
	}
      else if (strcmp(args[k], "CT2")  == 0)
	{
	  if (dupAddendum(cs1Lists, cs2Lists, cs3Lists, otherLists, k))
	    return false;
	  otherLists[k] = cs2Lists;
	}
      else if (strcmp(args[k], "CT3")  == 0)
	{
	  if (dupAddendum(cs1Lists, cs2Lists, cs3Lists, otherLists, k))
	    return false;
	  otherLists[k] = cs3Lists;
	}
      else if (strcmp(args[k], "IP1")  == 0)
	{
	  if (dupAddendum(ipv1Lists, ipv2Lists, ipv3Lists, otherLists, k))
	    return false;
	  otherLists[k] = ipv1Lists;
	}
      else if (strcmp(args[k], "IP2")  == 0)
	{
	  if (dupAddendum(ipv1Lists, ipv2Lists, ipv3Lists, otherLists, k))
	    return false;
	  otherLists[k] = ipv2Lists;
	}
      else if (strcmp(args[k], "IP3")  == 0)
	{
	  if (dupAddendum(ipv1Lists, ipv2Lists, ipv3Lists, otherLists, k))
	    return false;
	  otherLists[k] = ipv3Lists;
	}
      else if (strcmp(args[k], "MC1")  == 0)
	{
	  if (dupAddendum(mc1Lists, mc2Lists, mc3Lists, otherLists, k))
	    return false;
	  otherLists[k] = mc1Lists;
	}
      else if (strcmp(args[k], "MC2")  == 0)
	{
	  if (dupAddendum(mc1Lists, mc2Lists, mc3Lists, otherLists, k))
	    return false;
	  otherLists[k] = mc2Lists;
	}
      else if (strcmp(args[k], "MC3")  == 0)
	{
	  if (dupAddendum(mc1Lists, mc2Lists, mc3Lists, otherLists, k))
	    return false;
	  otherLists[k] = mc3Lists;
	}
      else if (strcmp(args[k], "QI1")  == 0)
	{
	  if (dupAddendum(qis1Lists, qis2Lists, qis3Lists, otherLists, k))
	    return false;
	  otherLists[k] = qis1Lists;
	}
      else if (strcmp(args[k], "QI2")  == 0)
	{
	  if (dupAddendum(qis1Lists, qis2Lists, qis3Lists, otherLists, k))
	    return false;
	  otherLists[k] = qis2Lists;
	}
      else if (strcmp(args[k], "QI3")  == 0)
	{
	  if (dupAddendum(qis1Lists, qis2Lists, qis3Lists, otherLists, k))
	    return false;
	  otherLists[k] = qis3Lists;
	}
      else if (strcmp(args[k], "RY1")  == 0)
	{
	  if (dupAddendum(rt1Lists, rt2Lists, rt3Lists, otherLists, k))
	    return false;
	  otherLists[k] = rt1Lists;
	}
      else if (strcmp(args[k], "RY2")  == 0)
	{
	  if (dupAddendum(rt1Lists, rt2Lists, rt3Lists, otherLists, k))
	    return false;
	  otherLists[k] = rt2Lists;
	}
      else if (strcmp(args[k], "RY3")  == 0)
	{
	  if (dupAddendum(rt1Lists, rt2Lists, rt3Lists, otherLists, k))
	    return false;
	  otherLists[k] = rt3Lists;
	}
      else if (strcmp(args[k], "SF1")  == 0)
	{
	  if (dupAddendum(sga1Lists, sga2Lists, sga3Lists, otherLists, k))
	    return false;
	  otherLists[k] = sga1Lists;
	}
      else if (strcmp(args[k], "SF2")  == 0)
	{
	  if (dupAddendum(sga1Lists, sga2Lists, sga3Lists, otherLists, k))
	    return false;
	  otherLists[k] = sga2Lists;
	}
      else if (strcmp(args[k], "SF3")  == 0)
	{
	  if (dupAddendum(sga1Lists, sga2Lists, sga3Lists, otherLists, k))
	    return false;
	  otherLists[k] = sga3Lists;
	}
      else if (strcmp(args[k], "MU1")  == 0)
	{
	  if (dupAddendum(unc1Lists, unc2Lists, unc3Lists, otherLists, k))
	    return false;
	  otherLists[k] = unc1Lists;
	}
      else if (strcmp(args[k], "MU2")  == 0)
	{
	  if (dupAddendum(unc1Lists, unc2Lists, unc3Lists, otherLists, k))
	    return false;
	  otherLists[k] = unc2Lists;
	}
      else if (strcmp(args[k], "MU3")  == 0)
	{
	  if (dupAddendum(unc1Lists, unc2Lists, unc3Lists, otherLists, k))
	    return false;
	  otherLists[k] = unc3Lists;
	}
      else
	return false;
    }
  combineLists(mainList, otherLists, k);
  return true;
}
/********************************************************************/

} // namespace NDTU
